﻿//========================================================= 
/**@file FileWatcher.h
 * @brief 文件监控
 * 
 * @date 2018-08-20   09:14:31
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_FILEWATCHER_H_
#define _LIBZHOUYB_FILEWATCHER_H_
//--------------------------------------------------------- 
#include "../../../include/Base.h"

#include <thread>
#include <memory>
//--------------------------------------------------------- 
namespace zhou_yb {
namespace extension {
namespace ability {
//--------------------------------------------------------- 
/**
 * @brief 文件监控器
 * @date 2018-08-20 09:16
 */
class FileWatcher
{
public:
    /**
     * @brief 监听的回调函数
     * @date 2018-08-20 18:24
     */
    typedef void(*fpWatchNotify)(DWORD action, 
        const wchar_t* oldname, size_t oldlen, 
        const wchar_t* newname, size_t newlen);
protected:
    HANDLE _hDir;
    bool _isExit;
    DWORD _watchFlags;
    fpWatchNotify _callback;
    std::shared_ptr<std::thread> _thread;

    void watch_func()
    {
        byte buff[1024] = { 0 };
        FILE_NOTIFY_INFORMATION *pNotify = ctype_cast(FILE_NOTIFY_INFORMATION*)buff;
        DWORD cbBytes = 0;
        while(!_isExit)
        {
            /* FILE_NOTIFY_CHANGE_XXX */
            if(!ReadDirectoryChangesW(_hDir, &buff, sizeof(buff), IsWatchSubTree,
                _watchFlags, &cbBytes, NULL, NULL))
            {
                Timer::Wait(DEV_OPERATOR_INTERVAL);
                continue;
            }
            if(_callback)
            {
                const wchar_t* oldName = pNotify->FileName;
                size_t oldLen = pNotify->FileNameLength;
                const wchar_t* newName = nullptr;
                size_t newLen = 0;
                if(pNotify->NextEntryOffset != 0 && pNotify->FileNameLength > 0)
                {
                    PFILE_NOTIFY_INFORMATION p = (PFILE_NOTIFY_INFORMATION)((char*)pNotify + pNotify->NextEntryOffset);
                    newName = p->FileName;
                    newLen = p->FileNameLength;
                }
                _callback(pNotify->Action, oldName, oldLen, newName, newLen);
                memset(buff, 0, cbBytes);
            }
        }
    }
    inline bool is_valid()
    {
        return INVALID_HANDLE_VALUE != _hDir;
    }
public:
    FileWatcher() :
        _hDir(INVALID_HANDLE_VALUE),
        _watchFlags(0),
        _callback(nullptr),
        IsWatchSubTree(true) {}
    virtual ~FileWatcher()
    {
        Cancel();
    }
    /// 是否监控子目录
    bool IsWatchSubTree;
    /**
     * @brief 监控指定的事件
     * @date 2018-08-20 18:21
     * 
     * @param [in] dir 需要监听的文件目录
     * @param [in] flag 需要监听的状态 FILE_NOTIFY_CHANGE_XXX
     * @param [in] notify 监听事件回调
     */
    bool Watch(const char* dir, DWORD flag, fpWatchNotify notify)
    {
        if(_is_empty_or_null(dir))
            return false;

        CharConverter cvt;
        HANDLE handle = CreateFile(cvt.to_char_t(dir),
            FILE_LIST_DIRECTORY,
            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
            NULL,
            OPEN_EXISTING,
            FILE_FLAG_BACKUP_SEMANTICS,
            NULL);
        if(INVALID_HANDLE_VALUE == handle)
            return false;

        Cancel();
        _isExit = false;
        _hDir = handle;
        _watchFlags = flag;
        _callback = notify;
        _thread = std::make_shared<std::thread>(std::bind(&FileWatcher::watch_func, this));
        return true;
    }
    /// 监控所有事件
    bool Watch(const char* dir, fpWatchNotify notify)
    {
        return Watch(dir,
            FILE_NOTIFY_CHANGE_FILE_NAME |
            FILE_NOTIFY_CHANGE_DIR_NAME |
            FILE_NOTIFY_CHANGE_ATTRIBUTES |
            FILE_NOTIFY_CHANGE_SIZE |
            FILE_NOTIFY_CHANGE_LAST_WRITE |
            FILE_NOTIFY_CHANGE_LAST_ACCESS |
            FILE_NOTIFY_CHANGE_CREATION |
            FILE_NOTIFY_CHANGE_SECURITY,
            notify);
    }
    /// 等待监听时间
    void Wait()
    {
        if(_thread != nullptr)
        {
            _thread->join();
        }
    }
    /// 取消监听过程
    void Cancel()
    {
        _isExit = true;
        _thread = nullptr;

        if(is_valid())
        {
            CloseHandle(_hDir);
            _hDir = INVALID_HANDLE_VALUE;
        }
    }
};
//--------------------------------------------------------- 
} // namespace ability 
} // namespace extension 
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_FILEWATCHER_H_
//========================================================= 